/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is iRecipe code.
 *
 * The Initial Developer of the Original Code is Robert Morris.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */

package com.pavisoft.irecipe;

import org.omg.CORBA.UNKNOWN;

/**
 * @author Robert Morris
 *
 * Responsible for storing a number and a unit that collectively represent a
 * a quantity (e.g., of an ingredient), such as "2 cups".
 *
 */
public class Amount implements Cloneable
{
    
    private Quantity quantity;
    private Unit unit;
    private String unitNameIfOther; // used only if (this.unit == Unit.OTHER), e.g., "pinch" or "dash"
        
    public enum Unit
    {
        // Format:    //String array,  String,        String
        // UNIT_NAME (abbreviations, singular name, plural name)
        NO_UNIT (new String[0], "", ""), // would be used with, for example, "1 egg"
        OTHER (new String[0], "", ""), // unit not represented here, e.g., "dash"
        TEASPOON (new String[] {"tsp", "tsp."}, "teaspoon", "teaspoons"),
        TABLESPOON (new String[] {"Tbs", "Tbs.", "Tbl", "Tbl.", "Tblspn"}, "tablespoon", "tablespoons"),
        CUP (new String[] {"c.", "c"}, "cup", "cups"),
        PINT (new String[] {"pt", "pt."}, "pint", "pints"),
        QUART (new String[] {"qt", "qt."}, "quart", "quarts"),
        GALLON (new String[] {"gal", "gal."}, "gallon", "gallons"),
        OUNCE (new String[] {"oz.", "oz"}, "ounce", "ounce");
        
        // TODO: Add support for more international units above
        // TODO: Add ability to convert between units
        
        private String[] abbreviations;
        private String singularName;
        private String pluralName;
        
        Unit (String[] abbreviations, String singularName, String pluralName)
        {
            this.abbreviations = abbreviations;
            this.singularName = singularName;
            this.pluralName = pluralName;
        }
        
        /** Returns preferred abbreviation for this <code>Unit</code> */
        public String getAbbreviation()
        {
            return this.abbreviations[0];
        }
        
        /** Returns all abbreviations for this <code>Unit</code> */
        public String[] getAbbreviations()
        {
            return this.abbreviations;
        }
        
        public String getSingularName()
        {
            return this.singularName;
        }
        
        public String getPluralName()
        {
            return this.pluralName;
        }
    }
    
    /**
     * Creates a new instance of <code>Amount</code> with the specified
     *  quantity and unit.
     */
    public Amount(Quantity quantity, Unit unit)
    {
        this.quantity = quantity;
        this.unit = unit;        
    }
    
    public Amount(int quantityInt, Unit unit)
    {
        this(new Quantity(quantityInt), unit);
    }
    
    // Equals zero and NO_UNIT
    public Amount()
    {
        this(new Quantity(0), Unit.NO_UNIT);
    }
    
    /** Tries to parse the given string into a <code>Unit</code> by comparing it
     *  to known singular names, plural names, and abbreviations.
     *  @param stringToParse The string to parse
     *  @returns A <code>Unit</code> representing that of contained in the given
     *   string, or a <code>Unit</code> representing <code>Unit.OTHER</code> if
     *   the parsing did not find any unit.
     */
    public static Unit parseUnit(String stringToParse)
    {
        Unit unit = null;
        for (Unit u : Unit.values())
        {
            for (String abbrev : u.getAbbreviations())
            {
                if (abbrev.equals(stringToParse))
                {
                    unit = u;
                }
            }
            if (u.getSingularName().equals(stringToParse) ||
                u.getPluralName().equals(stringToParse))
            {
                unit = u;
            }
        }
        // if still not found:
        if (unit == null)
        {
            unit = Unit.OTHER;
        }
        return unit;
    }

    /** Returns the <code>Unit</code> associated with this <code>Amount</code>. */
    public Unit getUnit()
    {
        return unit;
    }

    /** Sets the <code>Unit</code> associated with this <code>Amount</code> */
    public void setUnit(Unit unit)
    {
        this.unit = unit;
    }
    
    /**
     * Returns a <code>Quantity</code> representing the <code>Quantity</code> associated
     *  with this <code>Amount</code>, e.g., 3 if this <code>Amount</code>
     *  represents 3 cups.
     */
    public Quantity getQuantity()
    {
        return this.quantity;
    }
    
    /**
     * 
     *  Returns a <code>Amount</code> that based on <code>startingAmount</code>
     *  that has been converted to measurement units specified by <code>toUnit</code>.
     * 
     * @param startingAmount The <code>Amount</code> from which to start
     * @param toUnit The <code>Unit</code> to which the specified <code>Amount</code>
     *                should be converted.
     * @returns A new <code>Amount</code> that represents the one given, converted
     *   to the specified units
     */
    public static Amount convert(Amount startingAmount, Unit toUnit)
    {
        throw new UnsupportedOperationException("Not yet implemented");
    }
    
    /** USE ONLY IF UNIT NOT RECOGNIZED. Sets this <code>Amount</code>'s <code>Unit</code>
     * porition to <code>Unit.OTHER</code>, and will use the string given in <code>name</code>
     * to name the unit whenever needed.
     */
    public void setUnknownUnitName(String name)
    {
        this.unitNameIfOther = name;
    }
    
    public Object clone()
    {
        return new Amount((Quantity)this.quantity.clone(), this.unit);
    }
    
    public String toString()
    {
        String s;
        s = this.quantity.toString();
        if (this.quantity.equals(0))
        {
            s = "";
        }
        else
        {
            s += " ";
        }
        if (this.unit != Unit.NO_UNIT && this.unit != unit.OTHER)
        {
            if (this.quantity.equals(1))
            {
                s += this.unit.getSingularName();
            }
            else
            {
                s += this.unit.getPluralName();
            }
        }
        else if (this.unit == unit.OTHER)
        {
            s += this.unitNameIfOther;
            if (s == null) s = "";
        }
        s = s.trim();
        return s;
    }
    
}
